<?php


namespace app\api\service\impl;


use app\api\logic\CouponLogic;
use app\model\order\Order;
use app\model\order\OrderAddress;
use app\model\order\OrderDetail;
use app\model\pms\Goods;
use app\model\pms\GoodsSku;
use app\model\user\Cart;
use app\model\user\UserAddress;
use support\exception\BadException;
use support\utils\Decimal;
use support\utils\Time;
use think\db\exception\DbException;
use think\facade\Db;

/**
 * 普通订单
 * Class NormalOrderStrategy
 * @package app\api\service\impl
 */
class NormalOrderStrategy extends AbstractOrderStrategy
{
    public function preCreateOrder($data)
    {
        $uid = $data['uid'];
        $cart_ids = $data['cart_ids'] ?? ""; //购物车过来的
        $goods_id = $data['goods_id'] ?? ""; //直接购买
        $sku_id = $data['sku_id'] ?? ""; //直接购买
        $address_id = $data['address_id'] ?? ""; //前端变地址
        $coupon_id = $data['coupon_id'] ?? 0; //选中的卡券 计算金额
        $discountPriceDecimal = Decimal::newDecimal(); //优惠金额
        $totalCalc = Decimal::newDecimal();

        if (!$cart_ids) {
            $goods = Goods::where([
                'id' => $goods_id
            ])
                ->field([
                    'id',
                    'name',
                    'unit',
                    'main_image',
                    'freight_type',
                    'shipping_tpl_id',
                    'cate_id1',
                    'cate_id2',
                    'cate_id3',
                    'brand_id',
                    'tag_ids',
                    'freight',
                ])->find()
                ->toArray();

            $sku = GoodsSku::where([
                'sku_id' => $sku_id
            ])
                ->field([
                    'sku_id',
                    'goods_id',
                    'sku_name',
                    'price',
                    'price as total_price',
                    'image',
                    'weight',
                    'volume',
                ])->find()->toArray();
            $sku['num'] = 1;
            $goods = array_merge($goods, $sku);
            $goodsList = [$goods];
            $totalCalc->add(Decimal::newDecimal($sku['num'])->multiply($sku['total_price']));
        } else {
            $cartList = Cart::where('id', 'in', explode(',', $cart_ids))->select()->toArray();
            if (!$cartList) {
                throw new BadException("购物车没有数据");
            }
            $cartMap = array_column($cartList, 'num', 'sku_id');
            $goodsList = $this->getGoodsSkuList(array_column($cartList, 'sku_id'), $cartMap);
            foreach ($goodsList as $key => $item) {
                $goodsList[$key]['total_price'] = Decimal::newDecimal($item['price'])
                    ->multiply($goodsList[$key]['num'])
                    ->getValue();
                $totalCalc->add($goodsList[$key]['total_price']);
            }
        }
        $total_price = $totalCalc->getValue(); //商品总金额
        $coupon = $this->couponLogic->getCanUse($uid, $goodsList);
        //如果 $coupon_id 没值说明前端没有选择优惠券 那就默认选一张
        if ($coupon_id == 0 && $coupon && count($coupon) > 0) {
            $coupon_id = $coupon[0]['id'] ?? 0;
        }
        if ($coupon_id) {
            $calCoupon = $this->calCoupon($coupon_id, $total_price);
            $discountPriceDecimal->add($calCoupon['discountAmount']);
        }
        $activity = []; //营销活动

        $address = $this->getAddress($uid, $address_id); //用户地址信息

        $calcFreightResult = $this->calcFreight($address, $goodsList);
        $freight_price = $calcFreightResult['fee'];
        //实际付款金额
        $real_total_price = Decimal::newDecimal($total_price)
            ->add($freight_price)
            ->subtract($discountPriceDecimal->getValue())
            ->getValue();
        return [
            'goodsList' => $goodsList,
            'coupon_id' => $coupon_id, //默认选中卡券
            'couponList' => $coupon, //可以卡券列表
            'total_price' => $total_price,//合计金额
            'discount_price' => $discountPriceDecimal->getValue(),//优惠金额
            'freight_price' => $freight_price, //运费
            'real_total_price' => $real_total_price,//实付金额
            'address' => $address,
            'intra_region' => $calcFreightResult['intra_region'], //当前用户收货城市是否存在配送规则中
            'activity' => $activity,
        ];
    }

    public function createOrder($data)
    {
        $uid = $data['uid'];
        $cart_ids = $data['cart_ids'] ?? "";
        $inputGoods = $data['goodsList'] ?? [];
        $coupon_receive_log_id = $data['coupon_receive_log_id'] ?? 0;
        if (!$inputGoods) {
            throw new BadException("无下单商品");
        }
        $order_no = "D" . date("YmdHis") . str_pad(rand(0, 9999), 4, '0');
        $insertGoodsSku = [];
        $goods_ids = array_column($inputGoods, 'goods_id');
        $sku_ids = array_column($inputGoods, 'sku_id');
        $skuList = GoodsSku::where('sku_id', 'in', $sku_ids)->select()->toArray();
        $skuList = array_column($skuList, null, 'sku_id');
        $goodsInfos = Goods::where('id', 'in', $goods_ids)->select()->toArray();
        $goodsInfos = array_column($goodsInfos, null, 'id');
        $totalPriceDecimal = Decimal::newDecimal();
        $couponAmount = 0; //卡券优惠金额
        $coupon_id = 0;
        $inputGoods = array_column($inputGoods, null, 'sku_id');
        $goodsList = $this->getGoodsSkuList(array_column($inputGoods, 'sku_id'), array_column($inputGoods, 'num', 'sku_id'));

        if ($coupon_receive_log_id > 0) {
            $calCoupon = $this->calCoupon($coupon_receive_log_id, $totalPriceDecimal->getValue());
            $couponAmount = $calCoupon['discountAmount'];
            $coupon_id = $calCoupon['coupon_id'];

            $collectCouponGoodsResult = (new CouponLogic())->collectCouponGoods($goodsList, $calCoupon['couponInfo']);
            $goodsList = $this->calGoodsCouponAmount($collectCouponGoodsResult, $couponAmount, $goodsList);
        }
        foreach ($goodsList as $key => $value) {
            $curSku = $skuList[$value['sku_id']] ?? [];
            $curGoods = $goodsInfos[$value['goods_id']] ?? [];
            $curInputGoods = $inputGoods[$value['sku_id']] ?? [];
            if (!$curSku || !$curGoods || !$curInputGoods) {
                throw new BadException("商品数据异常");
            }
            if ($curGoods['is_show'] == 0 || $curGoods['is_del'] == 1) {
                throw new BadException("商品已经下架");
            }
            $curNum = $curInputGoods['num'];
            if ($curSku['stock'] <= $curNum) {
                throw new BadException("商品库存不足");
            }

            $insertGoodsSku[] = [
                "goods_id" => $curGoods['id'],
                "order_no" => $order_no,
                "goods_name" => $curGoods['name'],
                "logo" => $curSku['image'] ?: $curGoods['main_image'],
                "sku_id" => $curSku['sku_id'],
                "sku_name" => $curSku['sku_name'],
                "coupon_amount" => $value['coupon_amount'] ?? 0,
                "num" => $curNum,
                "price" => $curSku['price'],
                "total_goods_price" => Decimal::newDecimal($curSku['price'])
                    ->multiply($curNum)
                    ->getValue(),
            ];
            $totalPriceDecimal->add(Decimal::newDecimal($curNum)->multiply($curSku['price']));

            $goodsList[$key]['num'] = $curNum;
        }

        if (!isset($data['address_id']) || !$data['address_id']) {
            throw new BadException("请选择收货地址");
        }
        $userAddress = UserAddress::where(['id' => $data['address_id'], 'user_id' => $uid])
            ->find();

        $calcFreightResult = $this->calcFreight($userAddress, $goodsList);
        if (!$calcFreightResult['intra_region']) {
            throw new BadException("该收货地址无法配送");
        }
        $freight_price = $calcFreightResult['fee'];
        //创建订单
        $order_id = Order::insertGetId([
            "type" => Order::TYPE_NORMAL,
            "order_no" => $order_no,
            "user_id" => $uid,
            "total_num" => array_sum(array_column($goodsList, 'num')),
            "freight_price" => $freight_price,
            "order_price" => $totalPriceDecimal->getValue(), //订单商品金额合计
            "coupon_amount" => $couponAmount, //折扣金额
            "pay_price" => Decimal::newDecimal($totalPriceDecimal->getValue())
                ->add($freight_price)
                ->subtract($couponAmount)
                ->getValue(),//实际应该付款
            "pay_status" => Order::PAY_STATUS_WAIT,
            "status" => Order::STATUS_WAIT_PAID,
            "source" => "mp",
            "remark" => $data['remark'] ?? "",
            "coupon_id" => $coupon_id,
            "coupon_receive_log_id" => $coupon_receive_log_id,
            "create_time" => Time::now(),
            "update_time" => Time::now(),
        ]);

        //创建地址
        OrderAddress::insert([
            "order_id" => $order_id,
            "user_id" => $uid,
            "address_id" => $data['address_id'],
            "user_name" => $userAddress['real_name'],
            "phone" => $userAddress['mobile'],
            "province" => $userAddress['province'],
            "city" => $userAddress['city'],
            "county" => $userAddress['county'],
            "detail" => $userAddress['detail'],
            "post_code" => $userAddress['post_code'],
            "longitude" => $userAddress['longitude'],
            "latitude" => $userAddress['latitude'],
            "create_time" => Time::now(),
            "province_code" => $userAddress['province_code'],
            "city_code" => $userAddress['city_code'],
            "county_code" => $userAddress['county_code'],
            "street_code" => $userAddress['street_code'],
        ]);
        //创建订单商品
        array_walk($insertGoodsSku, function (&$v) use ($order_id) {
            $v['order_id'] = $order_id;
        });
        OrderDetail::insertAll($insertGoodsSku);

        $this->usedCoupon($uid, $coupon_receive_log_id, $order_id);
        //扣库存
        $this->decSkuStock($insertGoodsSku);

        if ($cart_ids) {
            Cart::where('id', 'in', explode(',', $cart_ids))->delete();
        }
        return $order_no;
    }

    /**
     * @param $sku_ids
     * @param $numArray [sku_id:num]
     * @return array
     * @throws BadException
     * @throws DbException
     */
    private function getGoodsSkuList($sku_ids, $numArray)
    {
        $sku = GoodsSku::where([
            ['sku_id', 'in', $sku_ids]
        ])
            ->field([
                'sku_id',
                'goods_id',
                'sku_name',
                'price',
                'image',
                'weight',
                'volume',
            ])->select()
            ->toArray();
        $goods = Goods::where([
            ['id', 'in', array_column($sku, 'goods_id')]
        ])
            ->field([
                'id',
                'name',
                'unit',
                'main_image',
                'freight_type',
                'shipping_tpl_id',
                'freight',
                'cate_id1',
                'cate_id2',
                'cate_id3',
                'brand_id',
                'tag_ids',
            ])->select()->toArray();
        $goods = array_column($goods, null, 'id');
        $goodsList = [];
        foreach ($sku as $key => $value) {
            if (!isset($goods[$value['goods_id']])) {
                throw new BadException("异常商品:" . $value['goods_id']);
            }
            $value['num'] = $numArray[$value['sku_id']] ?? 0;
            $value['total_price'] = Decimal::newDecimal($value['num'])->multiply($value['price'])->getValue();
            $goodsList[] = array_merge($goods[$value['goods_id']], $value);
        }
        return $goodsList;
    }

    /**
     * 扣减库存
     * @param $insertGoodsSku
     * @throws BadException
     * @throws DbException
     */
    private function decSkuStock($insertGoodsSku)
    {
        //扣库存
        foreach ($insertGoodsSku as $value) {
            $updateFlag = GoodsSku::where([
                ['sku_id', '=', $value['sku_id']],
                ['stock', '>', 0],
            ])->update([
                'stock' => Db::raw("stock - ({$value['num']})")
            ]);
            if (!$updateFlag) {
                throw new BadException("库存扣减失败,下单失败");
            }
            $updateFlag = Goods::where([
                ['id', '=', $value['goods_id']],
                ['stock', '>', 0],
            ])->update([
                'stock' => Db::raw("stock - ({$value['num']})")
            ]);
            if (!$updateFlag) {
                throw new BadException("库存扣减失败,下单失败");
            }
        }
    }
}